/*********************************************************************************

 Copyright 2006 MakingThings

 Licensed under the Apache License, 
 Version 2.0 (the "License"); you may not use this file except in compliance 
 with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0 
 
 Unless required by applicable law or agreed to in writing, software distributed
 under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 CONDITIONS OF ANY KIND, either express or implied. See the License for
 the specific language governing permissions and limitations under the License.

*********************************************************************************/

// MakingThings - Make Controller Board - 2006

/** \file analogin.c	
	AnalogIn - Analog to Digital Converter.
	Functions for reading the analog inputs on the MAKE Application Board.
*/

/* Library includes. */
#include <string.h>
#include <stdio.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

/* Hardware specific headers. */
#include "board.h"
#include "AT91SAM7X256.h"
#include "lib_AT91SAM7X256.h"

#include "analogin.h"
#include "analogin_internal.h"


/* The ISR can cause a context switch so is declared naked. */
void AnalogInIsrWrapper( void ) __attribute__ ((naked));


struct AnalogIn_ AnalogIn;

/*
	The AnalogIn subsystem converts 0-3.3V signals to 10-bit digital values.
	The analog to digital converters read incoming signals from 0 - 3.3V.  They are rated as 5V tolerant, 
  and indeed can momentarily withstand higher voltages, but will not return meaningful values for anything 
  above 3.3V.\n
*/

/**	
	Read the value of an analog input.
	@param index An integer specifying which input (0-7).
	@return The value as an integer (0 - 1023).
*/
int AnalogIn_GetValue( int index )
{
  int value;

  if (index < 0 || index >= ANALOGIN_CHANNELS)
    return -1;

  if (xSemaphoreTake(AnalogIn.semaphore, portMAX_DELAY) != pdTRUE)
    return -1;

  /* Third Step: Select the active channel */
  int mask = 1 << index; 
  AT91F_ADC_DisableChannel(AT91C_BASE_ADC, ~mask);
  AT91F_ADC_EnableChannel(AT91C_BASE_ADC, mask);
  
  /* Fourth Step: Start the conversion */
  AT91F_ADC_StartConversion(AT91C_BASE_ADC);

  /* wait */
  if (xSemaphoreTake( AnalogIn.doneSemaphore, portMAX_DELAY) != pdTRUE)
    return -1;

  value = AT91F_ADC_GetLastConvertedData(AT91C_BASE_ADC) & 0xFFFF;

  xSemaphoreGive(AnalogIn.semaphore);

  return value;
}

int AnalogIn_Init()
{
  // Set up
  AT91F_ADC_CfgModeReg(AT91C_BASE_ADC,
       AT91C_ADC_TRGEN_DIS | // Hardware Trigger Disabled
       AT91C_ADC_LOWRES_10_BIT | // 10 bit conversion
       AT91C_ADC_SLEEP_NORMAL_MODE | // SLEEP
       ( ( 9 << 8 ) & AT91C_ADC_PRESCAL ) | // Prescale rate (8 bits)
       ( ( 127 << 16 ) & AT91C_ADC_STARTUP ) | // Startup rate
       ( ( 127 << 24 ) & AT91C_ADC_SHTIM ) ); // Sample and Hold Time

  // Do the OS stuff
  AnalogIn.semaphore = xSemaphoreCreateMutex();

  // Create the semaphore that will be used to wake the calling process up 
  vSemaphoreCreateBinary(AnalogIn.doneSemaphore);
  xSemaphoreTake(AnalogIn.doneSemaphore, 0);

  // Initialize the interrupts
  // WAS AT91F_AIC_ConfigureIt( AT91C_ID_ADC, 3, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, ( void (*)( void ) ) AnalogInIsrWrapper );
  // Which is defined at the bottom of the AT91SAM7X256.h file
  AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_ADC, 4, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &AnalogInIsrWrapper);
  AT91F_ADC_EnableIt(AT91C_BASE_ADC, AT91C_ADC_DRDY); 
  AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_ADC);

  return 0;
}

/*-----------------------------------------------------------*/


void AnalogInIsrWrapper(void)
{
	/* Save the context of the interrupted task. */
	portSAVE_CONTEXT();

	/* Do the handler */
	{
        portCHAR cTaskWokenByPost = pdFALSE; 
    
        int status = AT91C_BASE_ADC->ADC_SR;
        if (status & AT91C_ADC_DRDY)
            cTaskWokenByPost = xSemaphoreGiveFromISR(AnalogIn.doneSemaphore, cTaskWokenByPost);
    
        int value = AT91C_BASE_ADC->ADC_LCDR;
        (void)value;
    
    	/* Clear AIC to complete ISR processing */
    	AT91C_BASE_AIC->AIC_EOICR = 0;
    
        /* do a task switch if necessary */
        if (cTaskWokenByPost == pdTRUE)
            portYIELD_FROM_ISR();
	}

	/* Restore the context of whichever task is going to run next. */
	portRESTORE_CONTEXT();
}
